#include <linux/slab.h>		/* kmalloc() */
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/init.h>

#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/namei.h>
#include <linux/dirent.h>
#include <linux/kobject.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/if_pppox.h>
#include <linux/list.h>
#include <linux/ptrace.h>
#include <linux/spinlock.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <net/tcp.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>	/* invalidate_bdev */
#include <linux/bio.h>
#include <linux/genhd.h>

#define BLKDEV_DATA_LEN (1024 * 1024 * 2)
typedef struct blkdev_data_s
{
    struct gendisk *disk;
    char *data;
    spinlock_t lock;    
    request_queue_t *queue;
} blkdev_data;


static int major;
static blkdev_data data;

static void dump_gendisk(struct gendisk *disk)
{
    
}

static void blkdev_transfer(blkdev_data *data, unsigned long sector,
		unsigned long nsect, char *buffer, int write)
{
	unsigned long offset = sector*KERNEL_SECTOR_SIZE;
	unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;

	if ((offset + nbytes) > BLKDEV_DATA_LEN) {
		printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
		return;
	}
	if (write)
		memcpy(data->data + offset, buffer, nbytes);
	else
		memcpy(buffer, data->data + offset, nbytes);
}

static void blkdev_request(request_queue_t *q)
{
	struct request *req;

	while ((req = elv_next_request(q)) != NULL) {
		struct blkdev_data *data = req->rq_disk->private_data;
		if (! blk_fs_request(req)) {
			printk (KERN_NOTICE "Skip non-fs request\n");
			end_request(req, 0);
			continue;
		}
    //    	printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
    //    			dev - Devices, rq_data_dir(req),
    //    			req->sector, req->current_nr_sectors,
    //    			req->flags);
		blkdev_transfer(data, req->sector, req->current_nr_sectors,
				req->buffer, rq_data_dir(req));
		end_request(req, 1);
	}
}

int blkdev_init(void)
{
	printk(KERN_DEBUG "%s enter\n", __FUNCTION__);    
    major = register_blkdev(0, "blkdev_test");
	printk(KERN_DEBUG "major = %d\n", major);
    memset(&data, 0, sizeof(blkdev_data));

    data.queue = blk_init_queue(blkdev_request, &data.lock);
    if (data.queue == NULL)
        goto err;
    
    data.disk = alloc_disk(major);
    if (data.disk == NULL) {
        goto err;
    }
	data.data = kmalloc(BLKDEV_DATA_LEN, GFP_KERNEL);
    if (data.data == NULL)
        goto err;
    data.disk->private_data = &data;
	spin_lock_init(&data.lock);
    
    dump_gendisk(data.disk);
    return 0;

err:
    unregister_blkdev(major, "blkdev_test");
    major = 0;

    if (data.queue == NULL) {
        blk_cleanup_queue(data.queue);
        data.queue = NULL;
    }

    if (data.disk) {
        del_gendisk(data.disk);
        data.disk = NULL;
    }
    if (data.data) {
        kfree(data.data);
        data.data = NULL;
    }
    
	printk(KERN_DEBUG "[%s] error\n", __FUNCTION__);
    return (-1);
}


void blkdev_exit(void)
{
    int ret;
	printk(KERN_DEBUG "%s exit\n", __FUNCTION__);
    if (major != 0) {
        ret = unregister_blkdev(major, "blkdev_test");
        printk(KERN_DEBUG "ret = %d\n", ret);
        major = 0;
    }
    if (data.queue == NULL) {
        blk_cleanup_queue(data.queue);
        data.queue = NULL;
    }

    if (data.disk) {
        del_gendisk(data.disk);
        data.disk = NULL;
    }    
    if (data.data) {
        kfree(data.data);
        data.data = NULL;
    }

}

module_init(blkdev_init);
module_exit(blkdev_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("tpl.a1406@gmail.com");

